iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 11
0
自我挑戰組

Some thing with Reason系列 第 11

Express-Router

  • 分享至 

  • xImage
  •  

Router

在上一篇中提到了 Middleware

今天會討論到更多細節的部分

但是第一步要先了解關於 Router

Express 可以拿來寫 RESTful API

但是 route 要如何分類呢?

雖然之前已經有用 app.get 來做基本的 route

但是如果大量使用的話其實會造成程式碼的雜亂跟難以維護

所以 Express 中有一個 Router 的 funciton

可以產出一個 Route 物件

然後再藉由 app.use 來做 route 的實現和管理

基本上可以這樣分類

分類方式沒有絕對答案

routes/userRoutes.js

const express = require('express');
const router = express.Router();

router.post('/', (req, res, _) => {
  res.json({name: 'tomas'});
});

module.exports = router;

app.js

const express = require('express');
const userRouter = require('./routes/usersRoutes');
const app = express();

const port = 5000;

app.use('/user', userRouter);

app.get('/', (req, res) => res.send('Hello World!'));

app.listen(port, () => console.log(`Example app listening on port ${port}!`));

這時候如果我們用 POST 呼叫 http://localhost:5000/user

得到的 respnose 會是 {"name": "tomas"}

我想這部分應該沒什麼問題

然後我們按照之前的模式把這些程式碼改為 .re

routes/UserRoutes.re

type response = {. "name": string };
type res = {.
  [@bs.meth] "send": string => string,
  [@bs.meth] "json": response => string
 };
type nextFun = unit => string;
type handler = (string, res, nextFun) => string;

type router = {.
  [@bs.meth] "get": (string, handler) => string,
  [@bs.meth] "use": handler => unit
};

[@bs.module "express"] external router: unit => router = "Router";


let routes = router();

routes##get("/", (_, res, _) => {
  let resp = {"name": "tomas"};
  res##json(resp);
});
type res = {.
  [@bs.meth] "send": string => string
 };

type nextFun = unit => string;
type handler = (string, res, nextFun) => string;

type expressApp = {.
  [@bs.meth] "use": handler => unit,
  [@bs.meth] "listen": (int) => unit,
  [@bs.meth] "get": (string, handler, handler) => string
};

type router = {.
  [@bs.meth] "get": (string, handler) => string,
  [@bs.meth] "use": handler => unit
};

[@bs.module] external express: unit => expressApp = "express";
[@bs.module "./routes/UserRoutes.bs"] external userRoutes: router = "routes";

let app = express();

[@bs.val] external use: handler => unit = "app.use";
[@bs.val] external useWithString: (string, router) => unit = "app.use";

use((_, _, next) => {
  Js.log("here is use function");
  next();
});

useWithString("/user", userRoutes);

app##get("/", (_, _, next) => {
  Js.log("hello next");
  next();
}, (_, res, _) => res##send("Hello World!"));

app##listen(5000);

首先遇到幾個問題

  • 一個是如何 require 正確的 routes/UserRoutes.bs 中的 routes?
    • 延伸怎麼控制 export ? 只能用 Reason 預設的?
  • app.use 有兩種 input 格式 如何處理?
[@bs.module "./routes/UserRoutes.bs"] external userRoutes: router = "routes";

第一個問題基本上上述那一行就可以解決

Import And Export

之前有提過如何引用共用模組

今天來談談本地的模組引用問題

Reason 中如何控制 export 呢?

BuckleScript 支援

  • CommonJS (require('MyFile'))
  • ES6 modules (import myFile from 'MyFile')
  • AMD(define(['myFile'], ...)

預設的 let 綁定的值都會自動 export

所以其他檔案都可以使用

Export ES6 value

example:

student.js

export default name = "Al";

teacher.js

import studentName from 'student.js';

在 Reason 中只需要用 let 綁定 default 這個變數

就可以自動輸出 ES6 預設模組

FavoriteStudent.re

let default = "default value";

demo.js

import studentName from 'FavoriteStudent.js';

Import

使用 @bs.module

[@bs.module "path"] external dirname : string => string = "dirname";
let root = dirname("/User/chenglou");

output

var Path = require("path");
var root = Path.dirname("/User/chenglou");

Import Default value

[@bs.module] external leftPad : string => int => string = "./leftPad";
let paddedResult = leftPad("hi", 5);

output

var LeftPad = require("./leftPad");
var paddedResult = LeftPad("hi", 5);

Import an ES6 Default value

[@bs.module "./student"] external studentName : string = "default";
Js.log(studentName);
const Student = require("./student");
console.log(Student.default);

當你在 BuckleScript 端使用的名稱和 JS 名稱相匹配

你可以使用空字串

[@bs.module "path"] external dirname : string => string = "";

相同的 Function 不同的參數

我們可以看到 app.userouter.use 中有兩種不同的輸入值

app.use((req, res, next) => {
  next();
});

app.use('/user', userRouter);

如上例可以看到 相同的 app.use 卻有兩種不同的輸入值得型態

在強形態中不能將這種 視為同樣的一個 function

所以要將它分為兩種 function 型態

type handler = (string, res, nextFun) => string;
type router = {.
  [@bs.meth] "get": (string, handler) => string,
  [@bs.meth] "use": handler => unit
};

[@bs.val] external use: handler => unit = "app.use";
[@bs.val] external useWithString: (string, router) => unit = "app.use";

可以看到 use 的 參數是放入一個 handler 的類型

而另一個 useWithString 也是 app.use

但是參數的類型是另外的 (string, router) => unit

第一個參數是字串 第二個是 router 的類型

裡面有一個 getuse 的 method

當然若是要實現 RESTful API 要再增加 post, put, delete....

不過方式也都一樣

也就不贅述了

Reason 可以發布到 NPM 上麻?

明天再來研究看看吧


上一篇
Express-Middleware
下一篇
Express-Mongodb
系列文
Some thing with Reason30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言